/* $RCSFile: ModuleInstall.cs $
 * Apache license v2 2011 by Christian Surieux
 * $Source: ?/ADNCRM.Core/Helpers/ModuleInstall.cs $
 * $log$
 * Revision 6 2010/08/06 19:56:28  christian.surieux
 *   Added Log to All cs files
 */

using System;
using System.Globalization;
using System.Diagnostics;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Reflection;
using System.Web;
using System.Xml;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Data.SqlClient;
using AddonNice.UI;
using AddonNice.Settings;
using AddonNice.UI.WebControls;
using AddonNice.Security;
using AddonNice.Configuration;
using AddonNice.Diagnostics;

using AddonNice.CRMWrapper;

using Trace2    =   System.Diagnostics.Trace;
using Path2     =   AddonNice.Settings.Path;

namespace AddonNice.Helpers
{
    public interface IPackageInstaller
    {
        bool Install(CRMWrapperBase wrapper,int portalID);
    }

    /// <summary>
	/// PackageInstall incapsulates all the logic for install packages on portal
	/// </summary>
	public class PackageInstall
	{
        public string packageName   =   string.Empty;
        public string ProductName   =   string.Empty;
        public int      Release     =   -1;
        public string CoreVersion   =   string.Empty;
        public string Version       =   string.Empty;
        public DateTime ReleaseDate =   DateTime.MinValue;
        public bool fatalErr        =   false;
        public int nbInstalled      =   0;
        DatabaseDB packDB           =   new DatabaseDB();
        Hashtable instalTb          =   new Hashtable();
        public ArrayList errors     =   new ArrayList();

        bool isCoreOkFor(string neededVersion)
        {
            if ( string.IsNullOrEmpty(neededVersion) )
                return true;
            string[] lastVTb        =   neededVersion.Split(new char[]{'.'},StringSplitOptions.RemoveEmptyEntries);
            string productVersion   =   PortalSettings.ProductVersion;
            string[] VTb            =   productVersion.Split(new char[]{'.'},StringSplitOptions.RemoveEmptyEntries);
            int maxIdx              =   lastVTb.GetLength(0);
            if ( VTb.GetLength(0) < maxIdx )
                maxIdx              =   VTb.GetLength(0);
            for (int i = 0; i < maxIdx; i++)
            {
                if ( lastVTb[i] == "*" )
                    break;
                if (int.Parse(lastVTb[i]) > int.Parse(VTb[i]))
                    return false;
            }
            return true;
        }
        
        bool isMoreRecent(string lastVersion, string Version)
        {
            if ( InstallTraceSwitch.Sw.TraceVerbose )
                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall isMoreRecent lastVersion: {0}, Version: {1}.", lastVersion,Version),InstallTraceSwitch.Sw.Info);
            if ( string.IsNullOrEmpty(Version) )
                return true;
            string[] lastVTb        =   lastVersion.Split(new char[]{'.'},StringSplitOptions.RemoveEmptyEntries);
            string[] VTb            =   Version.Split(new char[]{'.'},StringSplitOptions.RemoveEmptyEntries);
            int maxIdx              =   lastVTb.GetLength(0);
            if ( VTb.GetLength(0) < maxIdx )
                maxIdx              =   VTb.GetLength(0);
            bool ret                =   false;
            for (int i = 0; i < maxIdx && !ret ; i++)
            {
                if (int.Parse(lastVTb[i]) > int.Parse(VTb[i]))
                    ret             =   true;
            }
            if ( InstallTraceSwitch.Sw.TraceVerbose )
                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall isMoreRecent END lastVersion: {0} {2} Version: {1}.", lastVersion,Version, (ret) ? "<":">"),InstallTraceSwitch.Sw.Info);
            return ret;
        }
        
        DataTable BuildReleaseTable()
        {
            DataTable tb    =   new DataTable();
            tb.Columns.Add("Release",typeof(int));
            tb.Columns.Add("Version",typeof(string));
            tb.Columns.Add("CoreVersion",typeof(string));
            tb.Columns.Add("ReleaseDate",typeof(DateTime));
            tb.Columns.Add("Installed",typeof(bool));
            return tb;
        }

        public DataTable BuildInfoTb(string packageFilePath)
        {
            if ( InstallTraceSwitch.Sw.TraceVerbose )
                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall BuildInfo packageFilePath: {0}", packageFilePath),InstallTraceSwitch.Sw.Info);

            DataTable Tb                =   BuildReleaseTable();
            try
            {
                XmlDocument myDoc       =   new XmlDocument();
                string basePath         =   System.IO.Path.GetDirectoryName(packageFilePath);
                // load the history file
                myDoc.Load(packageFilePath);
                if (InstallTraceSwitch.Sw.TraceVerbose)
                    Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall BuildInfoTb myDoc.InnerXml: {0}", myDoc.InnerXml), InstallTraceSwitch.Sw.Info);

                // get package name
                XmlNode NameNd          =   myDoc.DocumentElement.SelectSingleNode("Name/text()");
                packageName             =   NameNd.Value;
                XmlNode PNameNd         =   myDoc.DocumentElement.SelectSingleNode("ProductName/text()");
                ProductName             =   PNameNd.Value;
                if (InstallTraceSwitch.Sw.TraceVerbose)
                    Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall BuildInfoTb packageName: {0}, ProductName: {1}", packageName,ProductName), InstallTraceSwitch.Sw.Info);
                // Try to find last version in DB for this package
                int lastRelease         =   -1;
                string lastVersion      =   string.Empty;
                DateTime lastReleaseDate = DateTime.MinValue;
                SqlDataReader dr        =   null;
                try
                {
                    dr                  =   packDB.GetCurrentPackage(packageName);
                    if (dr.HasRows && dr.Read())
                    {
                        lastRelease     =   (int)dr[StStr.DBRelease];
                        lastVersion     =   (string)dr[StStr.DBVersion];
                        lastReleaseDate = (DateTime)dr[StStr.DBReleaseDate];
                    }
                }
                catch (Exception ex1)
                {
                    // could be a first install
                    if (InstallTraceSwitch.Sw.TraceError)
                        Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]PackageInstall BuildInfoTb reading last DB version ex: {0}", ex1), InstallTraceSwitch.Sw.Info);
                }
                finally
                {
                    if (dr != null)
                        dr.Close();
                }
                // get a list of <Release> nodes
                XmlNodeList releases    =   myDoc.DocumentElement.SelectNodes("History/Release");

                // iterate over the <Release> nodes
                // They must be ordered in the file from oldest to latest on the end
                foreach (XmlNode releaseNd in releases)
                {
                    // get the header information
                    // we check for null to avoid exception if any of these nodes are not present
                    if (releaseNd.SelectSingleNode("ID") != null)
                        Release         =   Int32.Parse(releaseNd.SelectSingleNode("ID/text()").Value);
                    if (releaseNd.SelectSingleNode("CoreVersion") != null)
                        CoreVersion     =   releaseNd.SelectSingleNode("CoreVersion/text()").Value;
                    if (releaseNd.SelectSingleNode("Version") != null)
                        Version         =   releaseNd.SelectSingleNode("Version/text()").Value;
                    if (releaseNd.SelectSingleNode("Date") != null)
                        ReleaseDate     =   DateTime.Parse(releaseNd.SelectSingleNode("Date/text()").Value, CultureInfo.InvariantCulture);
                    DataRow rw          =   Tb.NewRow();
                    rw["Release"]       =   Release;
                    rw["Version"]       =   Version;
                    rw["CoreVersion"]   =   CoreVersion;
                    rw["ReleaseDate"]   =   ReleaseDate;

                    // Should we apply this patch
                    if (lastRelease < Release ||
                            ((lastRelease == Release) && isMoreRecent(lastVersion, Version))
                        )
                        rw["Installed"] = false;
                    else
                        rw["Installed"] = true;
                    Tb.Rows.Add(rw);
                }
            }
            catch (Exception ex)
            {
                if (InstallTraceSwitch.Sw.TraceError)
                    Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]PackageInstall BuildInfoTb ex: {0}", ex),InstallTraceSwitch.Sw.Info);
                fatalErr                =   true;
            }
        if ( InstallTraceSwitch.Sw.TraceVerbose )
            Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall BuildInfoTb END rowcount: {0}", Tb.Rows.Count),InstallTraceSwitch.Sw.Info);
        return Tb;
        }
        
        
        /// <summary>
        /// Installs a package from a xml file
        /// </summary>
		public bool InstallPackage(string packageFilePath,CRMWrapperBase wrapper,int portalID,int CrmServerId,ProgressDelegate fn,ProgressSetTotalSteps setTotSteps)
		{
            if ( InstallTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall InstallPackage BEG groupFileName: {0}", packageFilePath),InstallTraceSwitch.Sw.Info);

            try
            {
                XmlDocument myDoc                   =   new XmlDocument();
                ArrayList tempScriptsList           =   new ArrayList();
                string basePath                     =   System.IO.Path.GetDirectoryName(packageFilePath);
                // load the history file
                myDoc.Load(packageFilePath);
                if ( InstallTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall InstallPackage myDoc.InnerXml: {0}", myDoc.InnerXml),InstallTraceSwitch.Sw.Info);

                // // get package name
                XmlNode NameNd                      =   myDoc.DocumentElement.SelectSingleNode("Name/text()");
                packageName                         =   NameNd.Value;
                XmlNode PNameNd                     =   myDoc.DocumentElement.SelectSingleNode("ProductName/text()");
                ProductName                         =   PNameNd.Value;
                if ( InstallTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall InstallPackage packageName: {0}, ProductName: {1}", packageName,ProductName),InstallTraceSwitch.Sw.Info);
                // Try to find last version in DB for this package
                int lastRelease                     =   -1;
                string lastVersion                  =   string.Empty;
                DateTime lastReleaseDate            =   DateTime.MinValue;
                SqlDataReader dr                    =   null;
                try
                {
                    dr                              =   packDB.GetCurrentPackage(packageName);
                    if (dr.HasRows && dr.Read())
                    {
                        lastRelease                 =   (int)dr[StStr.DBRelease];
                        lastVersion                 =   (string)dr[StStr.DBVersion];
                        lastReleaseDate             =   (DateTime)dr[StStr.DBReleaseDate];
                    }
                }
                catch(Exception ex1)
                {
                    // could be a first install
                    if ( InstallTraceSwitch.Sw.TraceError )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]PackageInstall InstallPackage reading last DB version ex: {0}", ex1),InstallTraceSwitch.Sw.Info);
                }
                finally
                {
                    if ( dr!= null )
                        dr.Close();
                }

                if (InstallTraceSwitch.Sw.TraceVerbose)
                    Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall InstallPackage lastRelease: {0}, lastVersion: {1}, lastReleaseDate: {2}",
                        lastRelease,lastVersion,lastReleaseDate),InstallTraceSwitch.Sw.Info);
                // Make a backup of all customizations before installing modules
                bool ret                            =   wrapper.Util.BackupCustomizations();
                if (InstallTraceSwitch.Sw.TraceVerbose)
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall InstallPackage Backup Customizations success: {0}.",ret),InstallTraceSwitch.Sw.Info);
                // failed, we can't continue without backup
                if (!ret)
                {
                    string msg                      =   "[E]PackageInstall InstallPackage BackupCustomizations failed, stopping install.";
                    if (InstallTraceSwitch.Sw.TraceError)
                        Trace.WriteLine( msg,InstallTraceSwitch.Sw.Info);
                    throw new Exception(msg);
                }
                
                // get a list of <Release> nodes
                XmlNodeList releases                =   myDoc.DocumentElement.SelectNodes("History/Release");

                // iterate over the <Release> nodes
                // They must be ordered in the file from oldest to latest on the end
                foreach (XmlNode releaseNd in releases)
                {
                    // get the header information
                    // we check for null to avoid exception if any of these nodes are not present
                    if (releaseNd.SelectSingleNode("ID") != null)
                        Release                     =   Int32.Parse(releaseNd.SelectSingleNode("ID/text()").Value);
                    if (releaseNd.SelectSingleNode("CoreVersion") != null)
                    {
                        CoreVersion                 =   releaseNd.SelectSingleNode("CoreVersion/text()").Value;
                        if (!isCoreOkFor(CoreVersion))
                        {
                            errors.Add(string.Format("Core Version {0} needed",CoreVersion));
                            throw new Exception("Core version must be updated");
                        }
                    }
                    if (releaseNd.SelectSingleNode("Version") != null)
                        Version                     =   releaseNd.SelectSingleNode("Version/text()").Value;
                    if (releaseNd.SelectSingleNode("Date") != null)
                        ReleaseDate                 =   DateTime.Parse(releaseNd.SelectSingleNode("Date/text()").Value,CultureInfo.InvariantCulture);

                    // Should we apply this patch
                    if ( lastRelease < Release ||
                            ( (lastRelease == Release )&& isMoreRecent(lastVersion,Version) )
                        )
                    {
                        if (InstallTraceSwitch.Sw.TraceVerbose)
                            Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall InstallPackage Detected version to apply Release: {0}, Version: {1}",
                                Release,Version),InstallTraceSwitch.Sw.Info);
                        // get a list of <Sql> nodes
                        XmlNodeList sqlProcs        =   releaseNd.SelectNodes("Scripts/Sql");
                        if (InstallTraceSwitch.Sw.TraceVerbose)
                            Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall InstallPackage Slq scripts count: {0}", sqlProcs.Count),InstallTraceSwitch.Sw.Info);
                        // get a list of <SqlPost> nodes
                        XmlNodeList sqlPostProcs    =   releaseNd.SelectNodes("Scripts/PostSql");
                        if (InstallTraceSwitch.Sw.TraceVerbose)
                            Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall InstallPackage PostSlq scripts count: {0}", sqlPostProcs.Count),InstallTraceSwitch.Sw.Info);
                        // get a list of modules to install
                        XmlNodeList installers      =   releaseNd.SelectNodes("Modules/Module");
                        // get a list of starter to install in web config
                        XmlNodeList initSubsystems  =   releaseNd.SelectNodes("InitSubSystem");
                        // get a list of package specific claaa to call
                        XmlNodeList initPackage     =   releaseNd.SelectNodes("InitPackage");
                        // compute increment
                        int incCount                =   ( (sqlProcs.Count !=0) || (sqlPostProcs.Count !=0) || (installers.Count != 0) 
                            || ( initSubsystems.Count != 0 ) || ( initPackage.Count != 0 )) ?
                            (100 / (sqlProcs.Count+sqlPostProcs.Count+installers.Count+initSubsystems.Count+initPackage.Count)):100;

                        // iterate over the <Sql> Nodes (in original document order)
                        // Play each sql script against DB
                        foreach (XmlNode sqlProc0 in sqlProcs)
                        {
                            XmlElement sqlProcNd    =   (XmlElement)sqlProc0;
                            if ( fn != null )
                            {
                                string title        =   string.Format("Release: {0}, Version: {1}, script: {2}",Release,Version,sqlProcNd.InnerText);
                                fn(title,incCount);
                            }
                            if (InstallTraceSwitch.Sw.TraceVerbose)
                                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall InstallPackage Slq script name: '{0}', HasAttributes: {1}", 
                                    sqlProcNd.InnerText,sqlProcNd.HasAttributes),InstallTraceSwitch.Sw.Info);
                            bool transac            =   true;
                            if (sqlProcNd.HasAttributes && sqlProcNd.HasAttribute("NoTransaction") && 
                                bool.Parse(sqlProcNd.Attributes["NoTransaction"].Value))
                                transac             =   false;
                            string sqlProc          =   sqlProcNd.InnerText;
                            string currentScriptName    =   System.IO.Path.Combine(basePath,sqlProc);
                            if (InstallTraceSwitch.Sw.TraceVerbose)
                                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall InstallPackage applying currentScriptName: {0}", currentScriptName ),InstallTraceSwitch.Sw.Info);
                            DBHelper execScript     =   new AddonNice.Helpers.DBHelper();
                            try
                            {
                                execScript.ExecuteScript(currentScriptName, transac);
                            }
                            catch (Exception ex)
                            {
                                if (InstallTraceSwitch.Sw.TraceError)
                                    Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]PackageInstall InstallPackage installing Script: {0}, ex: {1}", currentScriptName, ex),InstallTraceSwitch.Sw.Info);
                                fatalErr                =   true;
                                throw;
                            }
                            finally
                            {
                                // Extracts errors
                                if (execScript.errors.Count > 0 )
                                    errors.AddRange(execScript.errors);
                            }
                        }
                        // modules
                        string modulesGPath         =   Config.ModulesPath.Replace("~/",string.Empty);
                        if ( InstallTraceSwitch.Sw.TraceVerbose )
                            Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall InstallPackage installers.Count: {0}, path: {1}",
                                installers.Count,modulesGPath),InstallTraceSwitch.Sw.Info);

                        // install each
                        foreach (XmlNode modNd in installers)
                        {
                            string friendlyName     =   modNd.SelectSingleNode("FriendlyName/text()").Value;
                            string desktopSource    =   modNd.SelectSingleNode("DesktopSource/text()").Value;
                            string usage            =   modNd.SelectSingleNode("Usage/text()").Value;
                            desktopSource.Replace("PageModules",modulesGPath);
                            if ( InstallTraceSwitch.Sw.TraceVerbose )
                                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall InstallPackage module friendlyName: {0}, desktopSource: {1}", friendlyName,desktopSource),InstallTraceSwitch.Sw.Info);
                            string title            =   string.Format("Release: {0}, Version: {1}, module: {2}",Release,Version,friendlyName);
                            fn(title,incCount);
                            ModuleInstall.Install(instalTb,friendlyName, desktopSource, usage, true,false,false,false,null);
                        }
                        ret                         =   wrapper.Util.PublishAll();
                        if (InstallTraceSwitch.Sw.TraceVerbose)
                            Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall InstallPackage PublishAll Customizations success: {0}.",ret),InstallTraceSwitch.Sw.Info);
                        // failed, we can't continue without backup
                        if (!ret)
                        {
                            string msg              =   "[E]PackageInstall InstallPackage PublishAll failed, stopping install.";
                            if (InstallTraceSwitch.Sw.TraceError)
                                Trace.WriteLine( msg,InstallTraceSwitch.Sw.Info);
                            throw new Exception(msg);
                        }

                        // iterate over the <SqlPost> Nodes (in original document order)
                        // Play each sql script against DB
                        foreach (XmlNode sqlProc0 in sqlPostProcs)
                        {
                            XmlElement sqlProcNd        =   (XmlElement)sqlProc0;
                            string title                =   string.Format("Release: {0}, Version: {1}, script: {2}",Release,Version,sqlProcNd.InnerText);
                            fn(title,incCount);
                            if (InstallTraceSwitch.Sw.TraceVerbose)
                                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall InstallPackage Slq Post script name: '{0}', HasAttributes: {1}", 
                                    sqlProcNd.InnerText,sqlProcNd.HasAttributes),InstallTraceSwitch.Sw.Info);
                            bool transac                =   true;
                            if (sqlProcNd.HasAttributes && sqlProcNd.HasAttribute("NoTransaction") && 
                                bool.Parse(sqlProcNd.Attributes["NoTransaction"].Value))
                                transac                 =   false;
                            string sqlProc              =   sqlProcNd.InnerText;
                            string currentScriptName    =   System.IO.Path.Combine(basePath,sqlProc);
                            if (InstallTraceSwitch.Sw.TraceVerbose)
                                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall InstallPackage applying Post currentScriptName: {0}", currentScriptName ),InstallTraceSwitch.Sw.Info);
                            DBHelper execScript         =   new AddonNice.Helpers.DBHelper();
                            try
                            {
                                execScript.ExecuteScript(currentScriptName, transac);
                            }
                            catch (Exception ex)
                            {
                                if (InstallTraceSwitch.Sw.TraceError)
                                    Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]PackageInstall InstallPackage installing Post Script: {0}, ex: {1}", currentScriptName, ex),InstallTraceSwitch.Sw.Info);
                                fatalErr            =   true;
                                throw;
                            }
                            finally
                            {
                                // Extracts errors
                                if (execScript.errors.Count > 0 )
                                    errors.AddRange(execScript.errors);
                            }
                        }
                        // Install now the new Crm custom entities
                        if ( InstallTraceSwitch.Sw.TraceVerbose )
                            Trace.WriteLine("[V]PackageInstall InstallPackage Installing Crm Entities.",InstallTraceSwitch.Sw.Info);
                        CrmInstaller installer                          =   null;
                        try
                        {
                            // no backup already done, but publish
                            installer                                   =   new CrmInstaller(packageName,wrapper,false,true,
                                                                                        delegate(string msg, int countInc)
                                                                                        { 
                                                                                            if ( InstallTraceSwitch.Sw.TraceVerbose )
                                                                                                Trace.WriteLine(string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall InstallPackage PROGRESS title: {0}.",msg),InstallTraceSwitch.Sw.Info);
                                                                                            fn(msg,countInc);
                                                                                        },
                                                                                        delegate(int newTot,int newVal)
                                                                                        { 
                                                                                            if ( InstallTraceSwitch.Sw.TraceVerbose )
                                                                                                Trace.WriteLine(string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall InstallPackage RESET PROGRESS newTot: {0}.",newTot),InstallTraceSwitch.Sw.Info);
                                                                                            setTotSteps(newTot,newVal);
                                                                                        }); 
                            // install new custom entities from the adn_crmEntity table
                            installer.InstallCrmServer(CrmServerId);

                            // Complete any Crm Iframe or Form for CrmServer and portal '0'
                            installer.InstallCrmUI(portalID,true,true,true);
                            
                            installer.PostInstall();

                        }
			            catch (CrmInstallException cex)
			            {
				            if (InstallTraceSwitch.Sw.TraceError)
                                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]PackageInstall InstallPackage Installing Crm Entities ex: {0}",cex),InstallTraceSwitch.Sw.Info);
				            if ( ( installer != null ) && !string.IsNullOrEmpty(installer.lastError) )
                                 errors.Add(installer.lastError);
                            else errors.Add(cex.Message);
                            throw;
			            }
			            catch (Exception ex)
			            {
				            if (InstallTraceSwitch.Sw.TraceError)
                                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]PackageInstall InstallPackage Installing Crm Entities  ex: {0}",ex),InstallTraceSwitch.Sw.Info);
                            errors.Add(ex.Message);
                            throw;
			            }
                        ///
                        // InitSubSystem in web.config : WARNING it stops the progress indicator by resetting session !!!
                        foreach (XmlNode modNd in initSubsystems)
                        {
                            string initClass        =   modNd.SelectSingleNode("InitClass/text()").Value;
                            string loadAssembly     =   modNd.SelectSingleNode("LoadAssembly/text()").Value;
                            if ( InstallTraceSwitch.Sw.TraceVerbose )
                                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall InstallPackage initSubsystem initClass: {0}, loadAssembly: {1}", initClass,loadAssembly),InstallTraceSwitch.Sw.Info);
                            string title            =   string.Format("InitSubsystems initClass: {0}, loadAssembly: {1}",initClass,loadAssembly);
                            fn(title,incCount);
                            InitSubSystem.CreateSection(ProductName,initClass,loadAssembly);
                        }
                        // Package specific installer
                        if ( InstallTraceSwitch.Sw.TraceVerbose )
                            Trace.WriteLine("[V]PackageInstall InstallPackage Installing dedicated package installer.",InstallTraceSwitch.Sw.Info);
                        foreach (XmlNode modNd in initPackage)
                        {
                            string initClass        =   modNd.SelectSingleNode("InitClass/text()").Value;
                            string loadAssembly     =   modNd.SelectSingleNode("LoadAssembly/text()").Value;
                            if ( InstallTraceSwitch.Sw.TraceVerbose )
                                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall InstallPackage initPackage initClass: {0}, loadAssembly: {1}", initClass,loadAssembly),InstallTraceSwitch.Sw.Info);
                            // useless until we find a way to avoid losing session
                            string title            =   string.Format("InitPackage initClass: {0}, loadAssembly: {1}",initClass,loadAssembly);
                            fn(title,incCount);
                            ExecPackageAssembly(initClass,loadAssembly,wrapper,portalID);
                        }
                        // insert the package in DB
                        try
                        {
                            packDB.AddPackage(packageName, Release, Version, ReleaseDate);
                        }
                        catch(Exception ex2)
                        {
                            // could be a first install
                            if ( InstallTraceSwitch.Sw.TraceError )
                                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]PackageInstall InstallPackage writing package in DB ex: {0}", ex2),InstallTraceSwitch.Sw.Info);
                            throw;
                        }
                        nbInstalled++; // one more release/version installed
                        if ( InstallTraceSwitch.Sw.TraceVerbose )
                            Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall InstallPackage nbInstalled: {0}", nbInstalled),InstallTraceSwitch.Sw.Info);
                    }
                }
            }
            catch (Exception ex)
            {
                if (InstallTraceSwitch.Sw.TraceError)
                    Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]PackageInstall InstallPackage ex: {0}", ex),InstallTraceSwitch.Sw.Info);
                fatalErr        =   true;
            }
            // return true for success
            return !fatalErr;
		}

        public bool ExecPackageAssembly(string className, string assembly,CRMWrapperBase wrapper,int portalID)
        {
            HttpRequest req         =   HttpContext.Current.Request;
            string appPath          =   req.ApplicationPath == "/" ? "" : req.ApplicationPath;

            //Search for assembly in bin dir
            string assemblyName     =   HttpContext.Current.Server.MapPath(appPath + "/bin/" + assembly);
            if ( InstallTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]PackageInstall ExecPackageAssembly className: {0}, assembly: {1}, assemblyName:{2}",
                    className, assembly, assemblyName),InstallTraceSwitch.Sw.Info);
            // Use reflection for loading assembly
            Assembly a              =   Assembly.LoadFrom(assemblyName);
            // Use reflection for create a Gateway instance
            IPackageInstaller Install =   null;
            bool ret                =   false;
            try
            {
                Install             =   (IPackageInstaller)a.CreateInstance(className);
                ret                 =   Install.Install(wrapper,portalID);
            }
            catch (Exception ex)
            {
                if ( InstallTraceSwitch.Sw.TraceError )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]PackageInstall ExecPackageAssembly  className: {0}, assembly: {1}, ex: {2}", className, assembly, ex),InstallTraceSwitch.Sw.Info);
                return false;
            }
            return ret;
        }

    }

	/// <summary>
	/// ModuleInstall encapsulates all the logic for install, 
	/// uninstall modules on portal
	/// </summary>
	public class ModuleInstall
	{
        /// <summary>
        /// Installs a group of modules from a xml file
        /// </summary>
		public static void InstallGroup(Hashtable instalTb,
            CRMWrapperBase wrapper,string groupFileName, bool install,ProgressDelegate fn)
		{
            if ( InstallTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleInstall InstallGroup groupFileName: {0}, install: {1}", groupFileName,install),InstallTraceSwitch.Sw.Info);
			DataTable modules           =   GetInstallGroup(groupFileName);
            // compute increment
            int incCount                =   ( (modules.Rows.Count !=0) ) ?
                (100 / (1+modules.Rows.Count)):100;
            string title                =   string.Empty;
            if ( fn != null )
            {
                title                   = string.Format("Backup customizations, before install for {0} modules",modules.Rows.Count);
                fn(title,incCount);
            }

            // Make a backup of all customizations before installing modules
            bool ret                    =   wrapper.Util.BackupCustomizations();
            if (InstallTraceSwitch.Sw.TraceVerbose)
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleInstall InstallGroup BackupCustomizations success: {0}.",ret),InstallTraceSwitch.Sw.Info);
            // failed, we can't continue without backup
            if (!ret)
            {
                string msg              =   "[E]ModuleInstall InstallGroup BackupCustomizations failed, stopping install.";
                if (InstallTraceSwitch.Sw.TraceError)
                    Trace.WriteLine( msg,InstallTraceSwitch.Sw.Info);
                throw new Exception(msg);
            }
            // do individual install for each module in the group
            foreach (DataRow r in modules.Rows)
			{
				string friendlyName		=   r[StStr.XMLFriendlyName].ToString();
				string desktopSource	=   r[StStr.XMLDesktopSource].ToString();
                if ( fn != null )
                {
                    title               =   string.Format("Installing {0}, path '{0}'",friendlyName,desktopSource);
                    fn(title,incCount);
                }

                // make any adaptation for modules's ascx location
                string modulesGPath     =   Config.ModulesPath.Replace("~/",string.Empty);
                desktopSource.Replace("PageModules",modulesGPath);
                
                string usage	        =   r[StStr.XMLUsage].ToString();

                if ( InstallTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleInstall InstallGroup friendlyName: {0}, install: {1}", friendlyName,desktopSource),InstallTraceSwitch.Sw.Info);
				Install(instalTb,friendlyName, desktopSource, usage, install,false,false,false,fn);
			}
            ret                         =   wrapper.Util.PublishAll();
            if (InstallTraceSwitch.Sw.TraceVerbose)
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleInstall InstallGroup PublishAll Customizations success: {0}.",ret),InstallTraceSwitch.Sw.Info);
            // failed, we can't continue without backup
            if (!ret)
            {
                string msg              =   "[E]ModuleInstall InstallGroup PublishAll failed, stopping install.";
                if (InstallTraceSwitch.Sw.TraceError)
                    Trace.WriteLine( msg,InstallTraceSwitch.Sw.Info);
                throw new Exception(msg);
            }
		}

	    
        /// <summary>
        /// Load the XML as dataset 
        /// </summary>
		private static DataTable GetInstallGroup(string groupFileName)
		{
			DataSet ds			=   new DataSet();
			string installer	=   groupFileName;

			try
			{
				ds.ReadXml(installer);
			}
			catch (Exception ex)
			{
				if ( GlobalTraceSwitch.Sw.TraceError )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]ModuleInstall GetInstallGroup groupFileName: {0}, ex: {1}",installer, ex),GlobalTraceSwitch.Sw.Info);
				throw;
			}

			return ds.Tables[0];
		}

		public static void UninstallGroup(IDictionary instalTb,CRMWrapperBase wrapper,string groupFileName, ProgressDelegate fn)
		{
			DataTable modules           =   GetInstallGroup(groupFileName);

            // Make a backup of all customizations before installing modules
            bool ret                    =   wrapper.Util.BackupCustomizations();
            if (InstallTraceSwitch.Sw.TraceVerbose)
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleInstall UninstallGroup Backup Customizations success: {0}.",ret),InstallTraceSwitch.Sw.Info);
            // failed, we can't continue without backup
            if (!ret)
            {
                string msg              =   "[E]ModuleInstall InstallPackage UninstallGroup failed, stopping install.";
                if (InstallTraceSwitch.Sw.TraceError)
                    Trace.WriteLine( msg,InstallTraceSwitch.Sw.Info);
                throw new Exception(msg);
            }
                
			foreach (DataRow r in modules.Rows)
			{
				string friendlyName		=   r[StStr.XMLFriendlyName].ToString();
				string desktopSource	=   r[StStr.XMLDesktopSource].ToString().ToLower(CultureInfo.InvariantCulture);
                
                string modulesGPath     =   Config.ModulesPath.Replace("~/",string.Empty);
                desktopSource.Replace("PageModules",modulesGPath);

				Uninstall(instalTb,desktopSource,false,false);
			}		
            ret                         =   wrapper.Util.PublishAll();
            if (InstallTraceSwitch.Sw.TraceVerbose)
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleInstall UninstallGroup PublishAll Customizations success: {0}.",ret),InstallTraceSwitch.Sw.Info);
            // failed, we can't continue without backup
            if (!ret)
            {
                string msg              =   "[E]ModuleInstall UninstallGroup PublishAll failed, stopping install.";
                if (InstallTraceSwitch.Sw.TraceError)
                    Trace.WriteLine( msg,InstallTraceSwitch.Sw.Info);
                throw new Exception(msg);
            }
		}

        /// <summary>
        /// Starts installing with transaction enabled
        /// </summary>
		public static void Install(IDictionary instalTb,string friendlyName, string desktopSource, string usage,ProgressDelegate fn)
		{
			Install(instalTb,friendlyName, desktopSource,usage,true,false,true,true,fn);
		}

		/// <summary>
		/// Do a simple install for one module, instanciate the module and call its installer
		/// </summary>
		public static void Install(IDictionary instalTb,string friendlyName, string desktopSource,string usage, 
            bool install,bool upgrade,bool backupCustomizations,bool publish,ProgressDelegate fn)
		{
			if ( GlobalTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleInstall Install BEG friendlyName: '{0}' from; '{1}, install: {2}, upgrade: {3}.",
                    friendlyName,desktopSource,install,upgrade),GlobalTraceSwitch.Sw.Info);
			// Dummy page necessary to load module
            AddonNice.UI.ADNPage page           =   new AddonNice.UI.ADNPage();
			// Instantiate the module
            PortalModuleControl portalModule	=   null;
            string controlPath                  =   AddonNice.Settings.Path.WebPathCombine(AddonNice.Settings.Path.ApplicationRoot, desktopSource);
            if ( InstallTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleInstall Install LoadControl: {0}.", controlPath),InstallTraceSwitch.Sw.Info);
            try
            {
                portalModule                    =   (PortalModuleControl)page.LoadControl(controlPath);
                if ( InstallTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleInstall Install control loaded: {0}.", controlPath),InstallTraceSwitch.Sw.Info);
                // insert in installDic with its GUI as key
                instalTb.Add(portalModule.GuidID,portalModule);
            }
            catch (Exception ex)
            {
                if ( InstallTraceSwitch.Sw.TraceError )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]ModuleInstall Install LoadControl controlPath: '{0}', ex: {1}", controlPath, ex),InstallTraceSwitch.Sw.Info);
                throw;
            }

			// Get Module ID
			Guid defID                          =	portalModule.GuidID;
            string Category                     =   portalModule.Category;            
			if ( InstallTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleInstall Install module loaded GuidID: {0}, Category: '{1}'", defID,Category),InstallTraceSwitch.Sw.Info);

			// Get Assembly name
            AssemblyName asm	                =   new AssemblyName(portalModule.GetType().BaseType.Assembly.FullName);
            string asmName			            =   asm.Name; //Get name only

			// Get Module Class name
			string className					=	portalModule.GetType().BaseType.FullName;
            
			if ( InstallTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleInstall Install module loaded asmName: {0}, className: '{1}'", asmName,className),InstallTraceSwitch.Sw.Info);

            // Now we add the definition to module list 
			ModulesDB modules					=	new ModulesDB();
            // New install or upgrade
			if ( install || upgrade ) 
			{
				//Install as new module
				try
				{
					if ( GlobalTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleInstall {0} '{1}' module",(upgrade ? "Upgrading":"Installing"),friendlyName),GlobalTraceSwitch.Sw.Info);
                    // module, installs itself
                    if ( upgrade )
					        portalModule.ReInstall(instalTb,backupCustomizations,publish);
					else    portalModule.Install(instalTb,backupCustomizations,publish);
				}
				catch (Exception ex)
				{
					if ( GlobalTraceSwitch.Sw.TraceError )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]ModuleInstall Installing Module '{0}' from '{1}', ex: {2}",friendlyName,desktopSource,ex),GlobalTraceSwitch.Sw.Info);
					portalModule.Rollback(instalTb);
					throw ;
				}
				// All is fine: we can call Commit
				portalModule.Commit(instalTb);
                if ( !upgrade )
                {
				    try
				    {
					    // Add a new module definition to the database
					    if ( GlobalTraceSwitch.Sw.TraceVerbose )
                            Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleInstall Install adding module definition to DB for '{0}'",friendlyName),GlobalTraceSwitch.Sw.Info);
                        modules.AddGeneralModuleDefinitions(defID, friendlyName, desktopSource, asmName, className,Category, portalModule.AdminModule,usage,(int)portalModule.ModuleType);
				    }
				    catch (Exception ex)
				    {
					    if ( GlobalTraceSwitch.Sw.TraceError )
                            Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]ModuleInstall Install AddGeneralModuleDefinitions failed for : {0}, ex: {1}",portalModule.GuidID,ex),GlobalTraceSwitch.Sw.Info);
					    throw;
				    }
                }
			}
            // update or upgrade: simply update the module definition, install proc is NOT run
			if ( !install ) 
			{
				try
				{
					if ( GlobalTraceSwitch.Sw.TraceInfo )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[I]ModuleInstall Install Updating {0} as existing module",friendlyName),GlobalTraceSwitch.Sw.Info);
                    modules.UpdateGeneralModuleDefinitions(defID, friendlyName, desktopSource, asmName, className,Category,portalModule.AdminModule,usage,(int)portalModule.ModuleType);
				}
				catch (Exception ex)
				{
					if ( GlobalTraceSwitch.Sw.TraceError )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]ModuleInstall Install UpdateGeneralModuleDefinitions failed GuidID: {0}, ex: {1}",portalModule.GuidID,ex),GlobalTraceSwitch.Sw.Info);
					throw ;
				}
			}
			// Insert or Update the module definition - install for portal 0
			modules.UpdateModuleDefinitions(defID, StStr.DefaultPortalID, true);
            // Do post install task, any task which necessitate the new module to be registered in portals
            if ( !upgrade )
    	        portalModule.PostInstall(instalTb,install,backupCustomizations,publish);
		}

        /// <summary>
        /// Uninstall a single module
        /// </summary>
		public static void Uninstall(IDictionary instalTb,string desktopSource,bool backupCustomizations,bool publish)
		{
            AddonNice.UI.ADNPage page           =   new AddonNice.UI.ADNPage();
			// Instantiate the module
            PortalModuleControl portalModule	=   null;
            string controlPath                  =   AddonNice.Settings.Path.WebPathCombine(AddonNice.Settings.Path.ApplicationRoot, desktopSource);
            if ( InstallTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleInstall Uninstall LoadControl : {0}.", controlPath),InstallTraceSwitch.Sw.Info);
            try
            {
                portalModule                    =   (PortalModuleControl)page.LoadControl(controlPath);
                if ( InstallTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleInstall Uninstall Control Loaded : {0}.", controlPath),InstallTraceSwitch.Sw.Info);
                // insert in installDic with its GUI as key
                instalTb.Add(portalModule.GuidID,portalModule);
            }
            catch (Exception ex)
            {
                if ( InstallTraceSwitch.Sw.TraceError )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]ModuleInstall Uninstall LoadControl controlPath : {0}, ex : {1}", controlPath, ex),InstallTraceSwitch.Sw.Info);
                throw;
            }

			//Call Uninstall
			try
			{
				portalModule.Uninstall(instalTb,backupCustomizations,publish);
				// Delete definition here CS
				new ModulesDB().DeleteModuleDefinition(portalModule.GuidID);
			}
			catch (Exception ex)
			{
				if ( GlobalTraceSwitch.Sw.TraceError )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]ModuleInstall Uninstall : {0}, ex: {1}",desktopSource,ex),GlobalTraceSwitch.Sw.Info);
				throw;
			}
		}
	}
}